﻿using Castle.Services.Transaction;
using Inovout.Models;
using NHibernate;
using NHibernate.Criterion;
using NHibernate.Linq;
using System.Collections.Generic;
using System.Linq;

namespace Inovout.Repositories
{
    public abstract class BaseRepository<TModel> : BaseRepository, IRepository, IRepository<TModel> where TModel : class
    {
        protected virtual ISession OpenSession()
        {
            return SessionManager.OpenSession();
        }

        public virtual void Save(TModel model)
        {
            ISession session = OpenSession();
            ITransactionManager transactionManager = ComponentRegistry.Resolve<ITransactionManager>();
            //if (transactionManager.CurrentTransaction == null)
            //{
            //    session.BeginTransaction();
            //}
            session.SaveOrUpdate(model);
            session.Flush();
            //if (transactionManager.CurrentTransaction == null)
            //{
            //    session.Transaction.Commit();
            //}
        }
        public virtual void Evict(TModel model)
        {
            ISession session = OpenSession();
            session.Evict(model);

        }
        public virtual void Delete(object id)
        {
            ISession session = OpenSession();
            //using (session.BeginTransaction())
            //{
            session.Delete(session.Load(typeof(TModel), id));
            session.Flush();
            //    session.Transaction.Commit();
            //}
        }
        public virtual void Delete(TModel model)
        {
            ISession session = OpenSession();
            session.Delete(model);
        }

        public virtual TModel FindById(object id)
        {
            ISession session = OpenSession();
            return session.Load<TModel>(id);
        }
        public virtual TModel LastOrDefault(string properyName)
        {
            IList<TModel> list = CreateCriteriaQuery().OrderBy(Order.Desc(properyName)).Top(1).Execute();

            if (list.Count == 0)
            {
                return null;
            }
            else if (list.Count == 1)
            {
                return list[0];
            }
            else
            {
                throw new ObjectNotFoundException(properyName, typeof(TModel));
            }
        }


        protected virtual CriteriaQuery<TModel> CreateCriteriaQuery()
        {
            ISession session = OpenSession();
            ICriteria sessionCriteria = session.CreateCriteria<TModel>();

            return new CriteriaQuery<TModel>(sessionCriteria);
        }
        public virtual IList<TModel> FindAll(params ICriterion[] criterions)
        {
            CriteriaQuery<TModel> criteriaQuery = CreateCriteriaQuery().Where(criterions);
            return criteriaQuery.Execute();
        }
        protected virtual ICriteria CreateCriteria()
        {
            ISession session = OpenSession();
            return session.CreateCriteria<TModel>(); ;
        }
        public virtual IDataObjectPagedSet<TModel> FindAll(int pageSize, int pageIndex, Order[] orders, params ICriterion[] criterions)
        {
            ICriteria criteria = CreateCriteria();
            foreach (ICriterion criterion in criterions)
            {
                criteria.Add(criterion);
            }
            var listCriteria = criteria.Clone() as ICriteria;
            int rowCount = criteria.SetProjection(
                Projections.RowCount()).UniqueResult<int>();

            listCriteria.SetFirstResult((pageIndex - 1) * pageSize);
            listCriteria.SetMaxResults(pageSize);
            if (orders != null)
            {
                foreach (Order order in orders)
                {
                    listCriteria.AddOrder(order);
                }
            }
            return new DataObjectPagedSet<TModel>(listCriteria.List<TModel>(), rowCount, pageSize, pageIndex);
        }
        public void Delete(params ICriterion[] criterions)
        {
            ISession session = OpenSession();
            foreach (TModel model in FindAll(criterions))
            {
                session.Delete(model);
            }
        }
        protected TModel Single(IList<TModel> list, ICriterion[] criterions = null)
        {
            if (list.Count == 0)
            {
                return null;
            }
            else if (list.Count == 1)
            {
                return list[0];
            }
            else
            {
                string identifier = string.Empty;
                if (criterions != null)
                {
                    foreach (var criterion in criterions)
                    {
                        identifier += criterion.ToString();
                        identifier += " ";
                    }
                }
                throw new ObjectNotFoundException(identifier, typeof(TModel));
            }
        }
        public TModel Find(params ICriterion[] criterions)
        {
            IList<TModel> list = FindAll(criterions);
            return Single(list);
        }
        public bool Exists(params ICriterion[] criterions)
        {
            return Count(criterions) > 0;
        }
        public int Count(params ICriterion[] criterions)
        {
            var countCriteria = OpenSession()
                  .CreateCriteria<TModel>();
            if (criterions != null)
            {
                foreach (var criterion in criterions)
                {
                    countCriteria.Add(criterion);
                }
            }
            return countCriteria.SetProjection(
                      Projections.Count(Projections.Id())
                  )
                  .UniqueResult<int>();
        }
        public virtual IEnumerable<TModel> FindbyKeyWord(string keyWord)
        {
            if (string.IsNullOrEmpty(keyWord))
            {
                return FindAll();
            }
            string[] keyWords = keyWord.Split(' ');
            ISession session = OpenSession();

            return null;

        }

        public virtual IQueryable<TModel> Query()
        {
            ISession session = OpenSession();
            IQueryable<TModel> queryable = session.Query<TModel>();
            queryable = queryable.Cacheable<TModel>();

            return queryable;
        }
        public abstract bool IsUnique(object id, string propertyName, object propertyValue, IDictionary<string, object> relationProperties = null);

        object IRepository.FindById(object id)
        {
            return FindById(id);
        }


        public bool IsUnique(object id, string propertyName, object propertyVallue)
        {
            return IsUnique(id, propertyName, propertyVallue, null);
        }
    }
}
